using FileServer.Abstractions;
using FileServer.Abstractions.Models;
using FileServer.Abstractions.Models.Entities;
using FileServer.Abstractions.Models.Options;
using FileServer.Abstractions.Models.Result;
using Gateway.IApplication.UploadFiles.Output;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.VisualBasic.FileIO;
using System.IO;
using System.IO.Compression;
using System.Xml.Linq;

namespace FileServer.Extension.Providers;

/// <summary>
/// 本地文件上传服务
/// </summary>
public class LocalFileUploadProvider : IFileUpload
{
    private readonly FileServerOptions _options;

    private readonly IFileStorage _storage;

    private readonly IServiceScopeFactory _serviceScopeFactory;

    public LocalFileUploadProvider(IOptions<FileServerOptions> options, IFileStorage storage, IServiceScopeFactory serviceScopeFactory)
    {
        _storage = storage;
        _options = options.Value;
        _serviceScopeFactory = serviceScopeFactory;
    }

    public async Task<UploadResult> UploadAsync(UploadFileInputStream input)
    {

        if (input.File == null)
        {
            throw new ArgumentException("文件不能为空");
        }
        Stream stream = new MemoryStream(input.File);
        // 先存储到 临时文件中
        UploadResult uploadResult = await CreateFileAsync(stream, input);
        return uploadResult;

    }

    public async Task<UploadResult> UploadAsync(UploadFileInputForm input)
    {

        if (input.File == null)
        {
            throw new ArgumentException("文件不能为空");
        }
        using (var stream = input.File?.OpenReadStream())
        {
            // 先存储到 临时文件中
            UploadResult uploadResult = await CreateFileAsync(stream, input);
            return uploadResult;
        }
    }

    public async Task<UploadResult> UploadAsync(UploadFileInputRemote input)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// 创建文件 并落库到 数据库中
    /// </summary>
    /// <param name="file"></param>
    /// <param name="input"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentException"></exception>
    private async Task<UploadResult> CreateFileAsync(Stream file, UploadFileInputBase input)
    {
        var extName = Path.GetExtension(input.FileName);

        if (file == null)
        {
            throw new ArgumentException("文件不能为空");
        }
        var fileInfo = await LocalFileUtil.CreateLocalFile(file, input, _options);

        string fileTmpPath = fileInfo.Path;
        string dirPath = Path.Combine(FileUploadProvider.LocalFilePerfix, DateTime.Now.ToString("yyyyMMdd"));
        string newFileName = fileInfo.FileNo + extName;
        string filePath = Path.Combine(dirPath, newFileName);

        await LocalFileUtil.MoveToPathAsync(fileInfo.Path, GetRawFilePath(dirPath), newFileName, true);
        // 落库
        fileInfo.Path = filePath;
        if (fileInfo.Path.Contains(@"\\"))
        {
            fileInfo.Path = fileInfo.Path.Replace(@"\\", "/");
        }
        if (fileInfo.Path.Contains(@"\"))
        {
            fileInfo.Path = fileInfo.Path.Replace(@"\", "/");
        }
        await _storage.Add(fileInfo);

        // 删除文件
        if (string.IsNullOrEmpty(fileTmpPath))
        {
            LocalFileUtil.TryDelete(fileTmpPath);
        }
        // 然后增加访问域名
        var fileRootPath = _options.FilePath;
        var newPath = fileInfo.Path.Replace(fileRootPath, "");
        FileProviderOptions uploadFileProviderOptions = FileUploadProvider.GetFileProviderOptions("local");
        if (uploadFileProviderOptions != null)
        {
            if (!newPath.StartsWith("/"))
            {
                newPath = "/" + newPath;
            }
            newPath = uploadFileProviderOptions.Domain + newPath;
        }
        return new UploadResult()
        {
            Url = newPath,
            Name = fileInfo.OriginName,
            Length = fileInfo.Length,
            FileId = fileInfo.Id
        };
    }


    /// <summary>
    /// 获取真实路径
    /// </summary>
    /// <param name="filePath"></param>
    /// <returns></returns>
    private string GetRawFilePath(string filePath)
    {
        var dirPath = Path.Combine(_options.FilePath, filePath);
        if (!Directory.Exists(dirPath))
        {
            Directory.CreateDirectory(dirPath);
        }

        return dirPath;
    }


    public async Task<bool> Delete(string fileUrl)
    {
        //后面考虑删除文件
        // 需要替换下路径
        string filePath = fileUrl;
        if (fileUrl.Contains("http"))
        {
            // 需要剔除 http
            string storageKey = FileUploadProvider.GetLocalFileStorageKey(fileUrl);
            var provider = FileUploadProvider.GetFileProviderOptions(storageKey);

            filePath = fileUrl.Replace(provider.MapDomain, "");
            if (filePath.StartsWith("/"))
            {
                filePath = filePath.TrimStart('/');
            }
        }
        return await _storage.Delete(filePath);
    }

    public async Task<bool> SaveTemp(string path)
    {
        // 需要替换掉 Private 字符串前面的内容 因为数据库存储了 Private/** 的格式
        path = path.Substring(path.IndexOf(FileUploadProvider.LocalFilePerfix));
        return await _storage.SaveTemp(path);
    }

    public async Task<bool> SaveTemp(List<string> paths)
    {
        // 需要替换掉 Private 字符串前面的内容 因为数据库存储了 Private/** 的格式
        for (int i = 0; i < paths.Count; i++)
        {
            var path = paths[i];
            path = path.Substring(path.IndexOf(FileUploadProvider.LocalFilePerfix));
        }
        return await _storage.SaveTemp(paths);
    }

    public async Task<MultipartUploadResult> PartUploadAsync(UploadPartInputStream stream)
    {
        string tmpFileName = string.Empty;
        string tmpFilePath = string.Empty;
        string uploadId = stream.UploadId;
        Stream fileStream = null;
        var extName = Path.GetExtension(stream.FileName);
        if (string.IsNullOrEmpty(extName))
        {
            extName = "";
        }
        if (extName.Length > 0)
        {
            extName = extName.Substring(1).ToLower();
        }
        try
        {
            byte[] buffer =
          Convert.FromBase64String(
              stream.Base64.Substring(stream.Base64.IndexOf(',') +
                                       1));
            fileStream = new MemoryStream(buffer);
            if (string.IsNullOrEmpty(stream.UploadId))
            {
                var fileInfo = await LocalFileUtil.CreatePartLocalFile(fileStream, stream, _options);
                uploadId = fileInfo.Id;
                string fileTmpPath = fileInfo.Path;
                string dirPath = Path.Combine(FileUploadProvider.LocalFilePerfix, DateTime.Now.ToString("yyyyMMdd"));
                string newFileName = fileInfo.FileNo + Path.GetExtension(stream.FileName); // "." + extName;
                string filePath = Path.Combine(dirPath, newFileName);
                // 落库
                fileInfo.Path = filePath;
                if (fileInfo.Path.Contains(@"\\"))
                {
                    fileInfo.Path = fileInfo.Path.Replace(@"\\", "/");
                }
                if (fileInfo.Path.Contains(@"\"))
                {
                    fileInfo.Path = fileInfo.Path.Replace(@"\", "/");
                }
                await _storage.Add(fileInfo);
            }
            else
            {
                var fileInfo = await _storage.Get(uploadId);
                if (fileInfo != null)
                {
                    tmpFileName = fileInfo.FileNo +
                                  Path.GetExtension(fileInfo.Path).ToLower();
                    tmpFilePath = fileInfo.Path;
                }
                await LocalFileUtil.CreatePartLocalFile(fileStream, stream, _options, fileInfo);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            fileStream?.Dispose();
        }
        return new MultipartUploadResult()
        {
            UploadId = uploadId
        };
    }

    public Task<string> FinishMultipartUploadAsync(UploadPartFinishRequest request, Action<FinishUploadResult> callback = null)
    {
        string uploadId = request.UploadId;
        _ = Task.Factory.StartNew(async () =>
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var repository = scope.ServiceProvider.GetService<IFileStorage>();
                var localFileInfo = await repository.Get(uploadId);
                if (localFileInfo != null)
                {
                    try
                    {
                        await MergePartLocalFileAsync(uploadId, localFileInfo.Path);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                    }

                    // 然后增加访问域名
                    var fileRootPath = _options.FilePath;
                    var newPath = localFileInfo.Path.Replace(fileRootPath, "");
                    FileProviderOptions uploadFileProviderOptions = FileUploadProvider.GetFileProviderOptions("local");
                    if (uploadFileProviderOptions != null)
                    {
                        if (!newPath.StartsWith("/"))
                        {
                            newPath = "/" + newPath;
                        }
                        newPath = uploadFileProviderOptions.Domain + newPath;
                    }

                    callback?.Invoke(new FinishUploadResult
                    {
                        Url = newPath,
                        Name = localFileInfo.OriginName
                    });
                }
            }
        }, TaskCreationOptions.LongRunning);
        return Task.FromResult<string>(uploadId);
    }

    /// <summary>
    /// 分片上传  合并分片文件
    /// </summary>
    /// <param name="uploadId">上传的id</param>
    /// <param name="targetFilePath">目标文件</param>
    /// <returns></returns>
    public async Task<bool> MergePartLocalFileAsync(string uploadId, string targetFilePath)
    {
        var tmpPath = LocalFileUtil.GetPartTempFilePath(uploadId, _options);
        //获取文件夹下的所有文件
        var fileList = Directory.GetFiles(tmpPath);
        if (!fileList.Any())
        {
            return false;
        }

        var partToken = "partNumber-";
        var mergeFiles = new List<MergeFileSort>();
        foreach (var file in fileList)
        {
            var sort = new MergeFileSort()
            {
                FileName = file
            };
            var tmpFileName = file.Split("--")[0];
            var fileIndex = tmpFileName.Substring(tmpFileName.IndexOf(partToken) + partToken.Length);
            var success = int.TryParse(fileIndex, out var number);
            if (!success)
            {
                continue;
            }

            sort.PartNumber = number;
            mergeFiles.Add(sort);
        }

        var mergeOrders = mergeFiles.OrderBy(s => s.PartNumber).ToList();
        // 合并文件
        var fileName = Path.GetFileName(targetFilePath);
        var fullTargetPath = GetRawFilePath(targetFilePath.Replace(fileName, "").TrimEnd('/'));
        var fullName = Path.Combine(fullTargetPath, fileName);
        var extName = Path.GetExtension(fileName);
        using (var fileStream = new FileStream(fullName, FileMode.Create))
        {
            foreach (var fileSort in mergeOrders)
            {
                using (FileStream fileChunk =
                       new FileStream(fileSort.FileName, FileMode.Open))
                {
                    await fileChunk.CopyToAsync(fileStream);
                }

                System.IO.File.Delete(fileSort.FileName);
            }
        }

        if (extName.ToLower().Equals(".zip") || extName.ToLower().Equals(".rar"))
        {
            var tempName = Path.Combine(fullTargetPath, Path.GetFileNameWithoutExtension(fileName));
            var backPath = tempName + "_backup" + extName;
            if (extName.ToLower().Equals(".rar"))
            {
                int bufferSize = 8192; // 缓冲区大小，可以根据需要进行调整

                using (FileStream sourceStream = File.OpenRead(fullName))
                using (FileStream destinationStream = File.Create(backPath))
                {
                    byte[] buffer = new byte[bufferSize];
                    int bytesRead;

                    while ((bytesRead = sourceStream.Read(buffer, 0, bufferSize)) > 0)
                    {
                        destinationStream.Write(buffer, 0, bytesRead);
                    }
                }
            }
            else
            {
                const int bufferSize = 1024 * 1024 * 50; // 设置缓冲区大小  50M
                byte[] buffer = new byte[bufferSize];
                int bytesRead;
                // // 使用 ZipFileExtensions 类的修复方法修复损坏的 ZIP 文件
                using (Ionic.Zip.ZipFile zipFile = new Ionic.Zip.ZipFile(fullName))
                {
                    using (FileStream sourceStream = File.OpenRead(fullName))
                    {
                        using (FileStream destinationStream = File.Create(backPath))
                        {
                            while ((bytesRead = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                destinationStream.Write(buffer, 0, bytesRead);
                            }
                        }
                    }

                    zipFile.Save(backPath);
                }
            }

            //如果处理成功 那么把原文件删除 然后再把backup文件重命名
            System.IO.File.Delete(fullName);
            File.Move(backPath, fullName);
        }


        //删除文件
        foreach (var file in mergeOrders)
        {
            if (File.Exists(file.FileName))
                System.IO.File.Delete(file.FileName);
        }

        var t = Path.Combine(tmpPath, "");
        Directory.Delete(t);
        return true;
    }

    public async Task<bool> DeleteById(string fileId)
    {
        //后面考虑删除文件
        return await _storage.DeleteById(fileId);
    }
}